
//---------------------------------------------------------------------------
#include "renderer.h"
//---------------------------------------------------------------------------
#include "vertexbuffer.h"

#include "pg2_indexbuffer.h"
//---------------------------------------------------------------------------
#include <d3dx9.h>
//---------------------------------------------------------------------------
using namespace ns;
//---------------------------------------------------------------------------
// vertex types definitions
#define D3DFVF_COLORVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
//---------------------------------------------------------------------------
// Engine to DirectX primitives enum mapping
D3DPRIMITIVETYPE g_ePrimitivesMapping[PrimitiveCount] = 
{
	D3DPT_TRIANGLELIST,		// TriangleList
	D3DPT_TRIANGLESTRIP,	// TriangleStrip
	D3DPT_POINTLIST,		// PointList
    D3DPT_LINELIST,			// LineList
    D3DPT_LINESTRIP,		// LineStrip
	D3DPT_TRIANGLEFAN		// TriangleFan
};
//---------------------------------------------------------------------------
Renderer::Renderer ()
:
m_pkD3D(NULL),
m_pkDevice(NULL),
CurrentVertexBuffer(NULL),
CurrentIndexBuffer(NULL)
{
	// nothing to do
}
//---------------------------------------------------------------------------
Renderer::~Renderer ()
{
	delete CurrentVertexBuffer;
	CurrentVertexBuffer = NULL;
	delete CurrentIndexBuffer;
	CurrentIndexBuffer = NULL;

}
//---------------------------------------------------------------------------
bool Renderer::init (HWND hWnd)
{
	_hWnd = hWnd;
	m_pkD3D = Direct3DCreate9(D3D_SDK_VERSION);

	if(!m_pkD3D)
	{
		return false;
	}

	D3DDISPLAYMODE displayMode;
	HRESULT hr;

	hr = m_pkD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode);

	if(hr != D3D_OK)
	{
		return false;
	}

	D3DPRESENT_PARAMETERS d3DPresentParameters;
	ZeroMemory(&d3DPresentParameters, sizeof(d3DPresentParameters));

	d3DPresentParameters.BackBufferFormat = displayMode.Format;
	d3DPresentParameters.Windowed = true;
	d3DPresentParameters.BackBufferCount = 1; 
	d3DPresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3DPresentParameters.hDeviceWindow = 0; 
	d3DPresentParameters.Flags = 0;
	d3DPresentParameters.EnableAutoDepthStencil = TRUE;
	d3DPresentParameters.AutoDepthStencilFormat = D3DFMT_D16;
	d3DPresentParameters.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3DPresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	hr = m_pkD3D->CreateDevice(
							  D3DADAPTER_DEFAULT,
 							  D3DDEVTYPE_HAL,							
							  hWnd,
							  D3DCREATE_HARDWARE_VERTEXPROCESSING,
							  &d3DPresentParameters,
							  &m_pkDevice
	);

	if(hr != D3D_OK)
	{
		return false;
	}

	// disable lighting
	m_pkDevice->SetRenderState(D3DRS_LIGHTING, 0);
	m_pkDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	//m_pkDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
	
	
	matProj = new D3DXMATRIX;
	viewMatrix = new D3DXMATRIX;

	//aspect ratio
	float fAspect = ((float)800) / 600;
	
	//punto inicial de camara
	D3DXVECTOR3 vEyePt(0.0f, 0.0f,0.0f );
	D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 1.0f );
	D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
	D3DXMatrixLookAtLH(viewMatrix, &vEyePt, &vLookatPt, &vUpVec );

	//seteo proyeccion a perspectiva
	D3DXMatrixPerspectiveFovLH( matProj, D3DX_PI/4, fAspect, 0.1f, 3000.0f );

	frustum = new D3DXPLANE[6];



	m_pkDevice->SetTransform( D3DTS_PROJECTION, matProj );
	m_pkDevice->SetTransform( D3DTS_VIEW, viewMatrix );
	return true;
}
//---------------------------------------------------------------------------
void Renderer::beginFrame ()
{
	m_pkDevice->BeginScene();
	m_pkDevice->Clear(0, 
					  NULL, 
					  D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
					  D3DCOLOR_XRGB(0,125,255),		// background color
					  1.0f, 
					  0
	);
}
//---------------------------------------------------------------------------
void Renderer::endFrame ()
{
	m_pkDevice->EndScene();
	m_pkDevice->Present(NULL, NULL, NULL, NULL);
}
//---------------------------------------------------------------------------
void Renderer::draw (const ColorVertex* pakVertexCollection, Primitive ePrim, unsigned int uiVertexCount)
{

}

void Renderer::drawCurrentBuffers (Primitive ePrimitive){
	// calculate primitive count
	int iPrimitiveCount = 0;

	D3DPRIMITIVETYPE ePrimitiveType = g_ePrimitivesMapping[ePrimitive];

	if(ePrimitiveType == D3DPT_POINTLIST)
	{
		iPrimitiveCount = CurrentIndexBuffer->indexCount();
	}
	else if(ePrimitiveType == D3DPT_LINELIST)
	{
		iPrimitiveCount = CurrentIndexBuffer->indexCount() / 2;
	}
	else if(ePrimitiveType == D3DPT_LINESTRIP)
	{
		iPrimitiveCount = CurrentIndexBuffer->indexCount() - 1;
	}
	else if(ePrimitiveType == D3DPT_TRIANGLELIST)
	{
		iPrimitiveCount = CurrentIndexBuffer->indexCount() / 3;
	}
	else if(ePrimitiveType == D3DPT_TRIANGLESTRIP)
	{
		iPrimitiveCount = CurrentIndexBuffer->indexCount() - 2;
	}
	else if(ePrimitiveType == D3DPT_TRIANGLEFAN)
	{
		iPrimitiveCount = CurrentIndexBuffer->indexCount() - 2;
	}

	// render
	HRESULT hr = m_pkDevice->DrawIndexedPrimitive(ePrimitiveType, 0, 0, 
												  CurrentVertexBuffer->vertexCount(), 
												  0, iPrimitiveCount
	);
	assert(hr == D3D_OK);
}
//---------------------------------------------------------------------------

void Renderer::UpdateView(float x,float y,float z,float lx,float ly,float lz){
	
	D3DXVECTOR3 vEyePt(x, y, z);
	D3DXVECTOR3 vLookatPt(lx, ly, lz );
	D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );

	D3DXMatrixLookAtLH( viewMatrix, &vEyePt, &vLookatPt, &vUpVec );
	m_pkDevice->SetTransform( D3DTS_VIEW, viewMatrix );

	BuildViewFrustum();

}

void Renderer::UpdateWorld(D3DXMATRIX* m)
{
	m_pkDevice->SetTransform( D3DTS_WORLD, m );
}

IDirect3DDevice9* Renderer::getDevice(){
	return m_pkDevice;
}

void Renderer::setCurrentVertexBuffer(VertexBuffer* CurrentVB){
	CurrentVertexBuffer = CurrentVB;
}

void Renderer::SetCurrentIndexBuffer(IndexBuffer* CurrentIB){
	CurrentIndexBuffer = CurrentIB;
}

void Renderer::BuildViewFrustum()
{
	D3DXMATRIX* matrix = new D3DXMATRIX;

	D3DXMatrixMultiply(matrix,viewMatrix,matProj);

    // Left plane
	frustum[0].a = matrix->_14 + matrix->_11;
    frustum[0].b = matrix->_24 + matrix->_21;
    frustum[0].c = matrix->_34 + matrix->_31;
    frustum[0].d = matrix->_44 + matrix->_41;
 
    // Right plane
    frustum[1].a = matrix->_14 - matrix->_11;
    frustum[1].b = matrix->_24 - matrix->_21;
    frustum[1].c = matrix->_34 - matrix->_31;
    frustum[1].d = matrix->_44 - matrix->_41;
 
    // Top plane
    frustum[2].a =matrix->_14 - matrix->_12;
    frustum[2].b =matrix->_24 - matrix->_22;
    frustum[2].c =matrix->_34 - matrix->_32;
    frustum[2].d =matrix->_44 - matrix->_42;
 
    // Bottom plane
    frustum[3].a = matrix->_14 + matrix->_12;
    frustum[3].b = matrix->_24 + matrix->_22;
    frustum[3].c = matrix->_34 + matrix->_32;
    frustum[3].d = matrix->_44 + matrix->_42;
 
    // Near plane
    frustum[4].a = matrix->_13;
    frustum[4].b = matrix->_23;
    frustum[4].c = matrix->_33;
    frustum[4].d = matrix->_43;
 
    // Far plane
    frustum[5].a = matrix->_14 - matrix->_13;
    frustum[5].b = matrix->_24 - matrix->_23;
    frustum[5].c = matrix->_34 - matrix->_33;
    frustum[5].d = matrix->_44 - matrix->_43;
 

    // Normalize planes
    for ( int i = 0; i < 6; i++ )
    {
        D3DXPlaneNormalize( &frustum[i], &frustum[i] );
    }
}

bool Renderer::BoxInFrustum(BoundingBox* box){
	for(int i=0 ; i<8 ; i++){
		bool inside = true;
		for(int j=0 ; j<6 ; j++){
			D3DXVECTOR3 vector;
			vector.x=box->GetVertices()[i].x;
			vector.y=box->GetVertices()[i].y;
			vector.z=box->GetVertices()[i].z;
			if(D3DXPlaneDotCoord(&frustum[j],&vector) < 0){
				inside = false;
			}
		}
		if(inside)
			return true;
	}
	return false;
}

HWND Renderer::GetWnd(){
	return _hWnd;
}

void Renderer::SetTexture(IDirect3DTexture9* texture){
	m_pkDevice->SetTexture(0,texture);
}